package top.zpengblog.db2document.jdbc;

import org.apache.commons.lang3.StringUtils;
import top.zpengblog.db2document.constant.ColumnConstant;
import top.zpengblog.db2document.constant.PrimaryKeyConstant;
import top.zpengblog.db2document.constant.SysConstant;
import top.zpengblog.db2document.constant.TableConstant;
import top.zpengblog.db2document.model.ColumnModel;
import top.zpengblog.db2document.model.ConnectionInfo;
import top.zpengblog.db2document.model.DataModel;
import top.zpengblog.db2document.model.PrimaryKeyModel;
import top.zpengblog.db2document.model.TableModel;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 采集元数据.
 *
 * @author ZhiPeng.Lin (codingjava@qq.com)
 * @version 1.0.0
 * @date 2022/1/28 20:13
 */
public abstract class AbstractMetadataCollector {

    public DataModel collectMetadata(ConnectionInfo connectionInfo) throws SQLException {
        Connection connection = ConnectionManager.getConnectionByCache(connectionInfo);
        if (connection == null) {
            throw new RuntimeException("获取数据库连接失败！");
        }
        DataModel dataModel = new DataModel();
        dataModel.setDatabase(connectionInfo.getDatabase());
        dataModel.setTitle(SysConstant.DEFAULT_FILE_NAME);
        dataModel.setDescription("");
        dataModel.setVersion("V" + SysConstant.VERSION_NUMBERS.get());
        dataModel.setOrganization("");
        dataModel.setOrganizationUrl("");
        List<TableModel> tableModels = getTables(connection);
        List<ColumnModel> columnModels = getColumns(connection);
        List<PrimaryKeyModel> primaryKeyModels = getPrimaries(connection);
        // 处理主键信息
        columnModels.forEach(item -> {
            String tableName = item.getTableName();
            String columnName = item.getColumnName();
            Optional<PrimaryKeyModel> keyModel = primaryKeyModels.stream()
                    .filter(primary -> StringUtils.equals(tableName, primary.getTableName()) && StringUtils.equals(columnName, primary.getColumnName()))
                    .findFirst();
            if (keyModel.isPresent()) {
                item.setPrimaryKey(SysConstant.Y);
            } else {
                item.setPrimaryKey(SysConstant.N);
            }
        });

        // 组装字段信息
        tableModels.forEach(tableModel -> {
            String tableName = tableModel.getTableName();
            List<ColumnModel> columnModelList = columnModels.stream().filter(columnModel -> StringUtils.equals(tableName, columnModel.getTableName()))
                    .collect(Collectors.toList());
            tableModel.setColumns(columnModelList);
        });
        dataModel.setTables(tableModels);
        return dataModel;
    }

    public abstract String getCataLog(Connection connection) throws SQLException;

    public abstract List<TableModel> getTables(Connection connection) throws SQLException;

    public abstract List<ColumnModel> getColumns(Connection connection)  throws SQLException;

    public abstract List<PrimaryKeyModel> getPrimaries(Connection connection)  throws SQLException;

    protected List<TableModel> handleTable(ResultSet resultSet) throws SQLException {
        List<TableModel> tableModelList = new ArrayList<>();
        while (resultSet.next()) {
            String name = resultSet.getString(TableConstant.TABLE_NAME);
            String remarks = resultSet.getString(TableConstant.TABLE_REMARK);
            String db = resultSet.getString(TableConstant.TABLE_CAT);
            TableModel tableModel = new TableModel();
            tableModel.setTableName(name);
            tableModel.setRemarks(remarks);
            tableModel.setDatabase(db);
            tableModelList.add(tableModel);
        }
        return tableModelList;
    }

    protected List<ColumnModel> handleColumn(ResultSet resultSet) throws SQLException {
        List<ColumnModel> columnModels = new ArrayList<>();
        while (resultSet.next()) {
            String tableName = resultSet.getString(ColumnConstant.TABLE_NAME);
            String remarks = resultSet.getString(ColumnConstant.REMARKS);
            String name = resultSet.getString(ColumnConstant.COLUMN_NAME);
            String columnSize = resultSet.getString(ColumnConstant.COLUMN_SIZE);
            String dec = resultSet.getString(ColumnConstant.DECIMAL_DIGITS);
            String decimal = StringUtils.isBlank(dec) ? dec : SysConstant.ZERO;
            String isNull = SysConstant.ZERO.equals(resultSet.getString(ColumnConstant.NULLABLE)) ? SysConstant.N : SysConstant.Y;
            String columnDefault = resultSet.getString(ColumnConstant.COLUMN_DEF);
            String index = resultSet.getString(ColumnConstant.ORDINAL_POSITION);
            String typeName = resultSet.getString(ColumnConstant.TYPE_NAME);
            ColumnModel columnModel = new ColumnModel();
            columnModel.setTableName(tableName);
            columnModel.setRemarks(remarks);
            columnModel.setColumnName(name);
            columnModel.setColumnSize(columnSize);
            columnModel.setDecimalDigits(decimal);
            columnModel.setNullable(isNull);
            columnModel.setColumnDef(columnDefault);
            columnModel.setOrdinalPosition(index);
            columnModel.setTableName(tableName);
            columnModel.setTypeName(typeName);
            columnModels.add(columnModel);
        }
        return columnModels;
    }

    protected List<PrimaryKeyModel> handlePrimary(ResultSet resultSet) throws SQLException {
        List<PrimaryKeyModel> primaryKeyModelList = new ArrayList<>();
        while (resultSet.next()) {
            PrimaryKeyModel primaryKeyModel = new PrimaryKeyModel();
            primaryKeyModel.setPkName(resultSet.getString(PrimaryKeyConstant.PK_NAME));
            primaryKeyModel.setTableCat(resultSet.getString(PrimaryKeyConstant.TABLE_CAT));
            primaryKeyModel.setTableName(resultSet.getString(PrimaryKeyConstant.TABLE_NAME));
            primaryKeyModel.setColumnName(resultSet.getString(PrimaryKeyConstant.COLUMN_NAME));
            primaryKeyModelList.add(primaryKeyModel);
        }
        return primaryKeyModelList;
    }
}
